/**
 * @author        Nicolas Bottarini <nicolasbottarini@gmail.com>, 404 <http://www.proyecto404.com>
 * @version       $Rev: 3 $
 * @lastrevision  $Date: 2011-09-12 16:45:37 +0000 (Mon, 12 Sep 2011) $	 
 */
package com.proyecto404.core.mvc {
	import com.greensock.TweenMax;
	import com.greensock.easing.Elastic;
	import com.proyecto404.core.BaseObject;
	import com.proyecto404.core.Configuration;
	import com.proyecto404.core.TranslationManager;
	import com.proyecto404.core.collections.Dictionary;
	import com.proyecto404.core.collections.IDictionary;
	import com.proyecto404.core.collections.IIterator;
	import com.proyecto404.core.exception.AbstractMethodException;
	import com.proyecto404.core.exception.InvalidOperationException;
	import com.proyecto404.core.log.fireflash.Console;
	import com.proyecto404.core.log.fireflash.LogSettings;
	import com.proyecto404.core.util.StringHelper;
	
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.system.ApplicationDomain;
	import flash.system.Capabilities;
	import flash.ui.Mouse;
	
	// TODO: Implementar flash.events.UncaughtErrorEvent cuando este el release del player 10.1
	// TODO: Ordenar un poco los metodos
	/**
	 * @abstract
	 */ 
	public class Application extends BaseObject {
		private static var _currentApplication:Application;
		
		private var _rootDisplayObject:DisplayObjectContainer;
		private var _popupsLayer:DisplayObjectContainer;
		private var _mousePointersLayer:DisplayObjectContainer;
		private var _configuration:Configuration;
		private var _translationManager:TranslationManager;
		private var _bootstrapper:Bootstrapper;
		private var _dataPath:String;
		private var _currentLanguage:String;
		private var _library:Library;
		private var _navigator:Navigator;
		private var _frontController:FrontController;
		private var _customMousePointer:DisplayObject;
		private var _applicationData:Dictionary;
		private var _viewFactory:IViewFactory;
		
		public function Application(rootDisplayObject:DisplayObjectContainer, debugMode:Boolean = false) {
			LogSettings.COMPILED_AS_ENABLED = debugMode;
			_bootstrapper = new Bootstrapper(onBootstrappingComplete);
			_rootDisplayObject = rootDisplayObject;
			_popupsLayer = new Sprite();
			_mousePointersLayer = new Sprite();
			_library = new Library(this);
			_frontController = new FrontController();
			
			if (isStandAlone()) {
				_dataPath = "";
				_currentLanguage = "es";
			} else {
				_dataPath = _rootDisplayObject.loaderInfo.parameters.dataPath;
				if (_dataPath == null) {
					//throw InvalidOperationException("dataPath flashvar must be declared (eg. dataPath = '/data/')");
					_dataPath = "";
				}
				if (!StringHelper.endsWith(_dataPath, "/") && _dataPath.length > 0) {
					_dataPath += "/";
				}
				_currentLanguage = _rootDisplayObject.loaderInfo.parameters.currentLanguage;
				if (_currentLanguage == null) {
					_currentLanguage = "es";
				}
			}

			_configuration = new Configuration(_dataPath);
			_translationManager = new TranslationManager(_currentLanguage);
			_applicationData = new Dictionary();
			viewFactory = new DefaultViewFactory(this);
			stage.addChild(_popupsLayer);
			stage.addChild(_mousePointersLayer);
		}
		
		public function get viewFactory():IViewFactory {
			return _viewFactory;
		}
		
		public function set viewFactory(value:IViewFactory):void {
			_viewFactory = value;;
		}

		public function getExternalParameter(name:String, defaultForStandAlone:String = ""):String {
			if (isStandAlone()) {
				return defaultForStandAlone;
			} else {
				return _rootDisplayObject.loaderInfo.parameters[name];
			}
		}		
		
		public function isStandAlone():Boolean {
			return Capabilities.playerType == "StandAlone";
		}
		
		public static function Run(app:Application):void {
			_currentApplication = app;
			app.registerBootstrappingTasks();
			app.bootstrapper.run();
		}
		
		public static function get Current():Application {
			return _currentApplication;
		}
		
		public function get configuration():Configuration {
			return _configuration;
		}
		
		public function get translations():TranslationManager {
			return _translationManager;
		}

		protected function get bootstrapper():Bootstrapper{
			return _bootstrapper;
		}

		public function get rootDisplayObject():DisplayObjectContainer {
			return _rootDisplayObject;
		}
		
		public function get popupsLayer():DisplayObjectContainer {
			return _popupsLayer;
		}

		public function get stage():Stage {
			return _rootDisplayObject.stage;
		}
		
		public function get library():Library {
			return _library;
		}
		
		public function get navigator():Navigator{
			return _navigator;
		}

		public function get dataPath():String {
			return _dataPath;
		}
		
		public function registerNotification(notificationName:String, action:Action):void {
			_frontController.registerNotification(notificationName, action);
		}

		public function dispatchNotification(notification:Notification):void {
			_frontController.dispatchNotification(notification);
		}
		
		public function changeMousePointer(newPointer:DisplayObject):void {
			_customMousePointer = newPointer;
			_customMousePointer.alpha = 0;
			_customMousePointer.scaleX = 0;
			_customMousePointer.scaleY = 0;
			_customMousePointer.x = stage.mouseX;
			_customMousePointer.y = stage.mouseY;
			stage.addChild(_customMousePointer);
			Mouse.hide();
			stage.addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
			stage.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
			TweenMax.to(_customMousePointer, 2, { alpha:1, scaleX:1, scaleY:1, ease:Elastic.easeOut});
		}
		
		private function mouseOutHandler(e:MouseEvent):void {
			TweenMax.to(_customMousePointer, 0.5, { alpha:0, scaleX:0, scaleY:0});
		}
		private function mouseOverHandler(e:MouseEvent):void {
			TweenMax.to(_customMousePointer, 2, { alpha:1, scaleX:1, scaleY:1, ease:Elastic.easeOut});
		}

		private function mouseMoveHandler(e:MouseEvent):void {
			_customMousePointer.x = e.stageX;
			_customMousePointer.y = e.stageY;
		}

		public function resetDefaultMousePointer():void {
			if (_customMousePointer == null) {
				return;
			}
			stage.removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
			stage.removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
			TweenMax.to(_customMousePointer, 0.5, { alpha:0, scaleX:0, scaleY:0, onComplete: function():void {
				stage.removeChild(_customMousePointer);
				Mouse.show();
				_customMousePointer = null;
			}});
		}
		/**
		 * @abstract
		 */
		protected function registerBootstrappingTasks():void { throw new AbstractMethodException(); }
		
		/**
		 * @abstract
		 */
		protected function initialize():void { throw new AbstractMethodException(); }

		private function onBootstrappingComplete():void {
			initialize();
			
			createNavigator();
			
			checkViewClassesDeclaration();
			
			executeStartupAction();			
		}
		
		private function createNavigator():void {
			var popupManager:PopupManager;
			if (configuration.hasConfiguration("settings:popupManagerClass")) {
				var popupManagerClassName:String = configuration["settings: popupManagerClass"];
				if (!ApplicationDomain.currentDomain.hasDefinition(popupManagerClassName)) {
					throw new InvalidOperationException("PopupManager class " + popupManagerClassName + " is not defined or not declared in declareRuntimeInstantiationClasses method");
				}
				var popupManagerClass:Class = ApplicationDomain.currentDomain.getDefinition(popupManagerClassName) as Class;
				popupManager = new popupManagerClass(_rootDisplayObject, _popupsLayer);
			} else {
				popupManager = new DefaultPopupManager(_rootDisplayObject, _popupsLayer);
			}
			
			_navigator = new Navigator(_rootDisplayObject, popupManager, this);
		}
		
		private function checkViewClassesDeclaration():void {
			if (configuration.hasSection("views")) {
				var viewsIt:IIterator = configuration["views"];
				while(viewsIt.hasNext()) {
					var viewInfo:ViewInfo = viewsIt.next() as ViewInfo;
					if (!ApplicationDomain.currentDomain.hasDefinition(viewInfo.className)) {
						throw new InvalidOperationException("You must declare class " + viewInfo.className + " in declareRuntimeInstantiationClasses method");
					}
				}
			}
		}
		
		private function executeStartupAction():void {
			if (!_frontController.isRegistered("startup")) {
				throw new InvalidOperationException("Startup Notification not registered");
			}
			dispatchNotification(new Notification(this, "startup", new Dictionary()));
		}
		
		public function get data():IDictionary {
			return _applicationData;
		}
	}
}